package com.suray.basic.wcs.path.core;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.suray.basic.wcs.node.WCSNodeModule;
import com.suray.basic.wcs.node.bo.WCSNodeModuleBO;
import com.suray.basic.wcs.node.bo.WCSNodeModuleRouteBO;
import com.suray.basic.wcs.path.enums.NodeStatus;
import com.suray.basic.wcs.path.enums.NodeType;
import com.suray.basic.wcs.time.bo.WCSTimeModuleBO;
import com.suray.basic.wcs.utils.Action;
import com.suray.basic.wcs.utils.Coord;
import com.suray.basic.wcs.utils.WcsFuncUtil;

import java.util.*;

public class Node extends Coord implements Comparable<Node> {
    public List<Action> actions = new ArrayList<>();
    private Action resAction;
    private NodeStatus nodeStatus;
    private NodeType nodeType;

    private Boolean up;

    private Boolean down;

    private Boolean left;

    private Boolean right;

    private Boolean canStop;

    private Boolean isDoor;
    // 该节点的父节点
    @JsonIgnore
    public Node parent;
    // 该节点的下个节点
    @JsonIgnore
    public Node preNode;
    // 该节点的下个节点
    @JsonIgnore
    public Node nextNode;

    // G：是个准确的值，是起点到当前结点的代价
    public long G;
    // H：是个估值，当前结点到目的结点的估计代价
    public long H;

    private int offsetX;

    private int offsetY;

    //smap中的x坐标
    private Integer seerMapX;

    //smap中的y坐标
    private Integer seerMapY;

    private String direction;

    public volatile int passTime;

    public int currentNodePassTime;
    @JsonIgnore
    private Set<Node> lockedNodeSet;
    @JsonIgnore
    private Set<Node> avoidLockedNodeSet;

    /**
     * 6代车点位标识
     */
    private String rgv60Point;

    /**
     * 6代车地码
     */
    private Integer groundCode;

    /**
     * 子车地码
     */
    private String sonRgvGroundCode;

    /**
     * 货叉高度
     * 货叉取货前的高度
     * 单位:cm
     */
    private Integer forkHeight;

    /**
     * 禁止通行的车类型集合
     */
    private List<Integer> forbidRgvs;

    private String script;

    /**
     * 节点行驶速度
     */
    private Integer speed;

    public String getScript() {
        return script;
    }

    public void setScript(String script) {
        this.script = script;
    }

    public Node(int x, int y, int z) {
        super(x, y, z);
    }

    public Node(Coord coord) {
        super(coord.getX(), coord.getY(), coord.getZ());
    }

    public Node(Coord coord, Node parent, long g, long h) {
        super(coord.getX(), coord.getY(), coord.getZ());
        this.parent = parent;
        G = g;
        H = h;
    }

    public Node(Coord coord, Node parent, int offsetX, int offsetY, String direction) {
        super(coord.getX(), coord.getY(), coord.getZ());
        this.parent = parent;
        this.offsetX = offsetX;
        this.offsetY = offsetY;
        this.direction = direction;
    }

    /**
     * 外部进行路径规划必须调用该构造器
     *
     * @param coord      节点坐标
     * @param nodeType   节点类型
     * @param nodeStatus 节点状态
     * @param up         可上
     * @param down       可下
     * @param left       可左
     * @param right      可右
     * @param forbidRgvs
     */
    public Node(Coord coord, NodeType nodeType, NodeStatus nodeStatus, Boolean up, Boolean down, Boolean left,
                Boolean right, Boolean canStop, Boolean isDoor, String rgvPoint,Integer groundCode, Integer forkHeight,
                String forbidRgvs) {
        super(coord.getX(), coord.getY(), coord.getZ());
        this.nodeStatus = nodeStatus;
        this.nodeType = nodeType;
        this.up = up;
        this.down = down;
        this.left = left;
        this.right = right;
        this.canStop = canStop;
        this.isDoor = isDoor;
        this.rgv60Point = rgvPoint;
        this.groundCode = groundCode;
        this.forkHeight = forkHeight;
        this.forbidRgvs = new ArrayList<>();
        if (StringUtils.isNotEmpty(forbidRgvs)) {
            try {
                for (String forbidRgv : forbidRgvs.split("-")) {
                    this.forbidRgvs.add(Integer.valueOf(forbidRgv));
                }
            } catch (Exception e) {
                WcsFuncUtil.exceptionLog(e, this.getClass());
                throw new RuntimeException("查询节点禁用车失败，存在安全风险，请查看节点配置:" + toString());
            }
        }
    }

    public Node(Node sourceNode) {
        super(sourceNode.getX(), sourceNode.getY(), sourceNode.getZ());
        this.nodeType = sourceNode.nodeType;
        this.isDoor = sourceNode.isDoor;
        this.resAction = sourceNode.getResAction();
    }

    public List<Integer> getForbidRgvs() {
        return forbidRgvs;
    }

    public void setForbidRgvs(List<Integer> forbidRgvs) {
        this.forbidRgvs = forbidRgvs;
    }

    public void addForbidRgv(int rgvType) {
        this.forbidRgvs.add(rgvType);
    }

    public void removeForbidRgv(Integer rgvType) {
        this.forbidRgvs.remove(rgvType);
    }

    public String analyzeForbidRgvStr() {
        String rgvTypes = "";
        for (Integer forbidRgv : forbidRgvs) {
            if (StringUtils.isNotEmpty(rgvTypes)) {
                rgvTypes += "-";
            }

            rgvTypes += forbidRgv;
        }

        return rgvTypes;
    }

    public Integer getSeerMapX() {
        return seerMapX;
    }

    public void setSeerMapX(Integer seerMapX) {
        this.seerMapX = seerMapX;
    }

    public Integer getSeerMapY() {
        return seerMapY;
    }

    public void setSeerMapY(Integer seerMapY) {
        this.seerMapY = seerMapY;
    }

    public String getRgv60Point() {
        return rgv60Point;
    }

    public void setRgv60Point(String rgv60Point) {
        this.rgv60Point = rgv60Point;
    }

    public Integer getGroundCode() {
        return groundCode;
    }

    public void setGroundCode(Integer groundCode) {
        this.groundCode = groundCode;
    }

    public String getSonRgvGroundCode() {
        return sonRgvGroundCode;
    }

    public void setSonRgvGroundCode(String sonRgvGroundCode) {
        this.sonRgvGroundCode = sonRgvGroundCode;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null) {
            return false;
        }
        Coord coord = (Coord) o;
        return this.getX() == coord.getX() && this.getY() == coord.getY() && this.getZ() == coord.getZ();
    }

    @Override
    public int compareTo(Node o) {
        if (o == null) {
            return -1;
        }
        long cGH = G + H;
        long oCH = o.G + o.H;
        if (Long.MAX_VALUE - G < H) {
            cGH = Long.MAX_VALUE;
        }
        if (Long.MAX_VALUE - o.G < o.H) {
            oCH = Long.MAX_VALUE;
        }
        if (cGH > oCH) {
            return 1;
        } else if (cGH < oCH) {
            return -1;
        }
        return 0;
    }

    /**
     * 与equals()同步
     *
     * @return hash值
     */
    @Override
    public int hashCode() {
        return Objects.hash(this.getX(), this.getY(), this.getZ());
    }

    /**
     * 标准字符串转换成Node对象
     *
     * @param NodeStr(x,y,z)
     * @return
     */
    public static Node parseGrid(String NodeStr) {
        try {
            String[] strings = NodeStr.split(",");
            int x = Integer.parseInt(strings[0].substring(1));
            int y = Integer.parseInt(strings[1]);
            int z = Integer.parseInt(strings[2].replace(")", ""));
            return new Node(x, y, z);
        } catch (Exception e) {
            throw new RuntimeException("解析字符串转Node异常" + e.getMessage());
        }
    }


    /**
     * 节点数组，字符串，转换成节点对象字符串
     *
     * @param coordsList
     * @return
     */
    public static String parseString(String[] coordsList) {
        StringBuilder stringBuilder = new StringBuilder();
        for (String s : coordsList) {
            stringBuilder.append(s + ";");
        }
        return stringBuilder.toString();
    }

    /**
     * 字符串转换成String的数组
     *
     * @param coords
     * @return
     */
    public static String[] parseList(String coords) {
        if (!StringUtils.isEmpty(coords)) {
            return coords.split(";");
        }
        return new String[0];
    }


    /**
     * 标准字符串转换成List<Node>对象
     *
     * @param NodeStr
     * @return
     */
    public static List<Node> parseGrids(String NodeStr) {
        List<Node> nodeList = new ArrayList<>();
        String[] stringsArray = NodeStr.split(";");
        try {
            for (String nodeStr : stringsArray) {
                Node node = parseGrid(nodeStr);
                nodeList.add(node);
            }
        } catch (Exception e) {
            throw new RuntimeException("解析字符串转Node异常" + e.getMessage());
        }
        return nodeList;
    }


    public Boolean getUp() {
        return up;
    }

    public void setUp(Boolean up) {
        this.up = up;
    }

    public Boolean getDown() {
        return down;
    }

    public void setDown(Boolean down) {
        this.down = down;
    }

    public Boolean getLeft() {
        return left;
    }

    public void setLeft(Boolean left) {
        this.left = left;
    }

    public Boolean getRight() {
        return right;
    }

    public void setRight(Boolean right) {
        this.right = right;
    }

    public void setCanStop(Boolean canStop) {
        this.canStop = canStop;
    }

    public Boolean getCanStop() {
        return canStop;
    }

    public void setIsDoor(Boolean isDoor) {
        this.isDoor = isDoor;
    }

    public Boolean getIsDoor() {
        return isDoor;
    }

    public NodeStatus getNodeStatus() {
        return nodeStatus;
    }

    public void setNodeStatus(NodeStatus nodeStatus) {
        this.nodeStatus = nodeStatus;
    }

    public NodeType getNodeType() {
        return nodeType;
    }

    public void setNodeType(NodeType nodeType) {
        this.nodeType = nodeType;
    }

    public Integer getSpeed() {
        return speed;
    }

    public void setSpeed(Integer speed) {
        this.speed = speed;
    }

    /**
     * 路径中的节点添加动作
     *
     * @param pathActions
     */
    public void addActions(Action... pathActions) {
        this.addActions(Arrays.asList(pathActions));
    }

    public List<Action> getActions() {
        return actions;
    }

    public void addActions(List<Action> actions) {
        actions.stream().filter(action -> !this.actions.contains(action)).forEach(action -> this.actions.add(action));
    }

    public Action getResAction() {
        return resAction;
    }

    public void setResAction(Action resAction) {
        this.resAction = resAction;
    }

    public static void main(String[] args) {
        System.out.println(parseGrid("(5,9,111,)").toString());
    }

    public void calculatePassTimeNoTask(int speed, int leadTime, int palletUpTime, int palletDownTime, int directionTime) {
        if (speed <= 0) {
            this.passTime = Integer.MAX_VALUE;
        } else {
            this.currentNodePassTime = 0;
            if (direction != null && (direction.contains("x") || direction.contains("X"))) {
                this.currentNodePassTime = getOffsetX() * 1000 / speed;
            } else {
                this.currentNodePassTime = getOffsetY() * 1000 / speed;
            }
            if (Integer.MAX_VALUE - leadTime < this.currentNodePassTime) {
                this.passTime = Integer.MAX_VALUE;
            } else {
                this.passTime = this.currentNodePassTime + leadTime;
            }
            int actionTime = actionTime(palletUpTime, palletDownTime, directionTime);
            if (Integer.MAX_VALUE - this.passTime < actionTime) {
                this.passTime = Integer.MAX_VALUE;
            } else {
                this.passTime = this.passTime + actionTime;
            }
        }
        if (this.nextNode != null) {
            this.nextNode.calculatePassTimeNoTask(speed, this.passTime, palletUpTime, palletDownTime, directionTime);
        }
    }

    public void calculatePassTime(int speed, int leadTime, int palletUpTime, int palletDownTime, int directionTime, Integer key) {
        if (speed <= 0) {
            this.passTime = Integer.MAX_VALUE;
        } else {
            this.passTime = leadTime;
            WCSNodeModuleBO wcsNodeModuleBO = WCSNodeModule.wcsNodeModuleMapCache.get(this.toString());
            if (wcsNodeModuleBO != null && wcsNodeModuleBO.getRouteBy().size() > 0) {
                WCSNodeModuleRouteBO wcsNodeModuleRouteBO = wcsNodeModuleBO.getRouteBy().get(key.toString());
                wcsNodeModuleBO.getRouteBy().entrySet().stream().forEach(x -> {
                    WCSNodeModuleRouteBO routeByItem = x.getValue();
                    if (!routeByItem.getKey().equals(key) && wcsNodeModuleRouteBO != null) {
                        if (wcsNodeModuleRouteBO.getLockTime() > routeByItem.getLockTime() && routeByItem.getPassTime() > wcsNodeModuleRouteBO.getLockTime()) {
                            if (Integer.MAX_VALUE - routeByItem.getCurrentPassTime() < this.passTime) {
                                this.passTime = Integer.MAX_VALUE;
                            } else {
                                this.passTime = this.passTime + routeByItem.getLockTime() - wcsNodeModuleRouteBO.getLockTime() + routeByItem.getCurrentPassTime();
                            }
                        }
                    }
                });
            }
            this.currentNodePassTime = 0;
            if (direction != null && (direction.contains("x") || direction.contains("X"))) {
                this.currentNodePassTime = getOffsetX() *1000 / speed;
            } else {
                this.currentNodePassTime = getOffsetY() * 1000 / speed;
            }
            if (Integer.MAX_VALUE - this.passTime < this.currentNodePassTime) {
                this.passTime = Integer.MAX_VALUE;
            } else {
                this.passTime = this.currentNodePassTime + this.passTime;
            }
            int actionTime = actionTime(palletUpTime, palletDownTime, directionTime);
            if (Integer.MAX_VALUE - this.passTime < actionTime) {
                this.passTime = Integer.MAX_VALUE;
            } else {
                this.passTime = this.passTime + actionTime;
            }
        }
        if (this.nextNode != null) {
            this.nextNode.calculatePassTime(speed, this.passTime, palletUpTime, palletDownTime, directionTime, key);
        }
    }

    public void cleanPassTime() {
        if (this.preNode != null) {
            this.preNode.cleanPassTime();
        }
    }

    public int actionTime(int palletUpTime, int palletDownTime, int directionTime) {
        int actionTime = 0;
        if (actions != null) {
            for (Action action : actions) {
                if (action == Action.CHANGE_TO_ROADWAY || action == Action.CHANGE_TO_RAMP) {
                    if (Integer.MAX_VALUE - actionTime < directionTime) {
                        actionTime = Integer.MAX_VALUE;
                    } else {
                        actionTime = actionTime + directionTime;
                    }
                } else if (action == Action.PALLET_UP) {
                    if (Integer.MAX_VALUE - actionTime < palletUpTime) {
                        actionTime = Integer.MAX_VALUE;
                    } else {
                        actionTime = actionTime + palletUpTime;
                    }
                } else if (action == Action.PALLET_DOWN) {
                    if (Integer.MAX_VALUE - actionTime < palletDownTime) {
                        actionTime = Integer.MAX_VALUE;
                    } else {
                        actionTime = actionTime + palletDownTime;
                    }
                }
            }
        }
        return actionTime;
    }

    public Integer findOffX() {
        return offsetX == 0 ? null : offsetX;
    }

    public Integer findOffY() {
        return offsetY == 0 ? null : offsetY;
    }

    public int getOffsetX() {
        if (WCSTimeModuleBO.OFFsetx != null && WCSTimeModuleBO.OFFsetx.get(this) != null)
            this.offsetX = WCSTimeModuleBO.OFFsetx.get(this);
        return offsetX;
    }

    public void setOffsetX(int offsetX) {
        this.offsetX = offsetX;
    }

    public int getOffsetY() {
        if (WCSTimeModuleBO.OFFsety != null && WCSTimeModuleBO.OFFsety.get(this) != null)
            this.offsetY = WCSTimeModuleBO.OFFsety.get(this);
        return offsetY;
    }

    public void setOffsetY(int offsetY) {
        this.offsetY = offsetY;
    }

    public String getDirection() {
        return direction;
    }

    public void setDirection(String direction) {
        this.direction = direction;
    }

    public Set<Node> getLockedNodeSet() {
        return lockedNodeSet;
    }

    public void setLockedNodeSet(Set<Node> lockedNodeSet) {
        this.lockedNodeSet = lockedNodeSet;
    }

    public Set<Node> getAvoidLockedNodeSet() {
        return avoidLockedNodeSet;
    }

    public void setAvoidLockedNodeSet(Set<Node> avoidLockedNodeSet) {
        this.avoidLockedNodeSet = avoidLockedNodeSet;
    }

    public String getDes() {
        return toString() + resAction;
    }

    public Float getForkHeight() {
        if (forkHeight == null) {
            return null;
        }

        return forkHeight.floatValue() / 1000;
    }

    public void setForkHeight(Integer forkHeight) {
        this.forkHeight = forkHeight;
    }

    public String print() {
        return toString() + "-" + rgv60Point + "-" + WcsFuncUtil.listToString(actions);
    }
}
